<template>
  <div class="cropper-container">
    <div class="cropper-avatar" @click="triggerFileInput">
      <div class="image" v-if="value">
        <img :src="value" alt="">
      </div>
      <div class="cropper-upload-holder" v-else><cloud-upload-outlined/></div>
      <input type="file" class="cropper-avatar-input" accept="image/*" @change="handleFileChange" ref="fileInput"/>
    </div>
    <div class="padding-top-sm text-color-gray" v-if="help">{{help}}</div>
    <a-modal title="裁剪预览" v-model:open="open" @cancel="cancelCrop" @ok="cropImage">
      <div class="cropper-preview" v-if="imageSrc">
        <VueCropper
            ref="cropper"
            :img="imageSrc"
            alt="Source Image"
            :view-mode="1"
            :auto-crop="true"
            :fixed="true"
            :fixedBox="true"
            :autoCropWidth="200"
            :autoCropHeight="200"
            :canMoveBox="false"
            class="cropper-preview-box"
            @realTime="realTime"
        />
        <div class="cropper-preview-image">
          <h3>实时预览</h3>
          <div class="preview-avatar">
            <img :src="previewUrl" v-if="previewUrl" alt="">
          </div>
          <div class="preview-avatar preview-avatar-circle">
            <img :src="previewUrl" v-if="previewUrl" alt="">
          </div>
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { VueCropper } from 'vue-cropper';
import 'vue-cropper/dist/index.css';
import {CloudUploadOutlined} from "@ant-design/icons-vue";
import request from "@/services/request.ts";
import {message} from "ant-design-vue";

// 定义上传响应数据类型
interface UploadResponse {
  url: string;
  [key: string]: any; // 允许其他属性
}

// 图片源
const imageSrc = ref('');
const fileInput = ref<HTMLInputElement | null>(null);
const cropper = ref<InstanceType<typeof VueCropper> | null>(null);
const open = ref<boolean>(false)
const previewUrl = ref<string>('')
const value = defineModel<string>('value')
defineProps({
  width:{type:Number,default:64},
  height:{type:Number,default:64},
  cropWidth:{type:Number,default:200},
  cropHeight:{type:Number,default:200},
  help:{type:String,default:'请上传 1:1 比例 10M 以内的PNG或JPG格式图片'},
})
//触发文件选择
const triggerFileInput = () => {
  fileInput.value?.click();
}
/**
 * 文件选择
 * @param event
 */
const handleFileChange = (event:Event) => {
  const target = event.target as HTMLInputElement;
  if (!target.files || target.files.length === 0) return;

  const file = target.files[0];
  if (file && file.type.startsWith('image/')) {
    const reader = new FileReader();
    reader.onload = (e: ProgressEvent<FileReader>) => {
      if (e.target?.result && typeof e.target.result === 'string') {
        imageSrc.value = e.target.result;
      }
    };
    reader.readAsDataURL(file);
  }
  setTimeout(() => open.value = true, 200)
};
const cropImage = async () => {
  if (!cropper.value) return;
  cropper.value.getCropBlob(async (blob: Blob | null) => {
    if (!blob) return;
    const formData = new FormData();
    formData.append('file', blob, 'cropped_image.jpg');
    try {
      // 使用 fetch 或 axios 上传
      const res:ApiResponse<UploadResponse> = await request.post('/uploader', formData);
     if(res.code === 200 && res.data){
       open.value = false;
       value.value = res.data.url;
       message.success('上传成功');
     }else {
      message.error(res.message);
     }
      imageSrc.value = '';
      if (fileInput.value) {
        fileInput.value.value = '';
      }
    } catch (error) {
      message.error('上传失败');
    }
  }, 'image/jpeg', 0.9); // JPEG 格式，质量 90%
};

// 实时预览函数
const realTime = () => {
  cropper.value.getCropBlob((data:Blob)=>{
    previewUrl.value = URL.createObjectURL(data)
  })
}

// 取消裁剪
const cancelCrop = () => {
  imageSrc.value = '';
  previewUrl.value = '';
  if (fileInput.value) {
    fileInput.value.value = '';
  }
};
</script>

<style scoped>
.cropper-avatar {
  width: 64px;
  height: 64px;
  border-radius: var(--border-radius);
  overflow: hidden;
  background-color: rgba(0, 0, 0, 0.02);
  border: 1px dashed #d9d9d9;
}
.cropper-avatar:hover{
  cursor: pointer;
  border-color: var(--color-primary);
  transition: var(--transition);
}
.cropper-avatar-input {
  display: none;
}
.cropper-preview {
  display: flex;
  height: 300px;
  gap: 24px;
}
.cropper-preview-box{
  width: 300px;
  height: 300px;
  border-radius: var(--border-radius);
  overflow: hidden
}
.cropper-preview-image {
  flex: 1;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.preview-avatar {
  width: 100px;
  height: 100px;
  overflow: hidden;
  border-radius: var(--border-radius);
  margin-bottom: 24px;
}
.preview-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.preview-avatar-circle {
  border-radius: 100px;
}
.cropper-upload-holder {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 20px;
  color: rgba(0, 0, 0, 0.45);
}
</style>